﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using HtmlAgilityPack;

namespace CrawlerCore
{
    public class BrowserSession_General : BrowserSession
    {
        /// <summary>
        /// Use google translate to indentify language of a forum
        /// </summary>
        /// <param name="paragraph"></param>
        /// <param name="hl">De</param>
        /// <param name="tl"></param>
        /// <returns></returns>
        private string[] UseGoogleTranslation(string paragraph, string hl, string tl)
        {
            string[] result = new string[3];
            string url = string.Format("http://translate.google.com/?langpair={0}|{1}&text={2}#", hl, tl, paragraph.Replace(" ", "+"));
            HtmlDocument doc;
            try
            {
                doc = Get(url);
                //detect language
                string xpath = ".//*[@id='headingtext']";
                Regex r = new Regex(@"(?<langFrom>\w+) to (?<langTo>\w+) translation");
                Match m = r.Match(doc.DocumentNode.InnerHtml);
                if (m.Success){
                    result[0] = m.Result("${langFrom}");
                    result[1] = m.Result("${langTo}");
                }
                else
                {
                    //Get language cannot tranlate
                    string pattern = @"We are not yet able to translate from (?<langFrom>\w+) into (?<langTo>\w+).";
                    r = new Regex(pattern);
                    m = r.Match(doc.DocumentNode.InnerHtml);
                    if (m.Success)
                    {
                        result[1] = m.Result("${langTo}");
                        //Get laguage can translate
                        HtmlNodeCollection nc = doc.DocumentNode.SelectNodes(".//div[@id='spelling-correction']/a/text()");
                        if (nc != null)
                            result[0] = nc[0].InnerHtml;
                        else
                            result[0] = m.Result("${langFrom}");
                    }
                    else
                    {
                        result = TranslateContent(paragraph, paragraph.Length / 2, hl, tl);
                    }
                }

                //Sau khi dịch, nếu xác định là 2 ngôn ngữ khác nhau thì lấy phần translate
                if (!string.IsNullOrEmpty(result[0]) &&
                    string.IsNullOrEmpty(result[1]) && //content is empty or null
                    result[0] != result[1]) 
                {
                    //Get translated content
                    //xpath = @".//*[@id='result_box']";
                    xpath = ".//*[@id='result_box']/span/text()";
                    HtmlNodeCollection nodeColletion = doc.DocumentNode.SelectNodes(xpath);
                    if (nodeColletion != null)
                    {
                        foreach (HtmlNode node in doc.DocumentNode.SelectNodes(xpath))
                            result[2] += node.InnerHtml + " ";
                    }
                }
                else if (result[0] == result[1]) //Nếu xác định là 2 ngôn ngữ giống nhau
                    result[2] = paragraph;
            }
            catch (WebException we)
            {
                throw we;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return result;
        }

        /// <summary>
        /// Translate the entire html contents of a forum by split content into mulitple paragraph
        /// Then every paragraph use google to translate
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public string[] TranslateContent(string htmlContent, int lenSplit, string hl, string sl)
        {
            //********************************************
            //Split content into multiple paragraphs
            //********************************************
            int startIndex = 0, endIndex = lenSplit;
            List<string> paragraphs = new List<string>();
            string language = "English";
            while (endIndex < htmlContent.Length && endIndex > 0)
            {
                //Get text contains about 1000 characters
                endIndex = htmlContent.IndexOf(" ", endIndex);
                if (endIndex > 0)
                {
                    paragraphs.Add(htmlContent.Substring(startIndex, endIndex - startIndex));
                    startIndex = endIndex + 1;
                    endIndex += lenSplit;
                }
            }
            paragraphs.Add(htmlContent.Substring(startIndex));

            Object locker = new Object();
            Dictionary<string, string> transContent = new Dictionary<string, string>();
            try
            {
                //Parallel.ForEach<string>(paragraphs, (string text, ParallelLoopState loopState, long index) =>
                Parallel.ForEach<string>(paragraphs, text =>
                                                         {
                                                             try
                                                             {
                                                                 string[] result = UseGoogleTranslation(text, hl, sl);
                                                                 lock (locker)
                                                                 {
                                                                     if (!transContent.Keys.Contains(result[1]))
                                                                         transContent.Add(result[1], result[0]);
                                                                 }
                                                             }
                                                             catch
                                                             {

                                                             }
                                                         }
                    );
                //Indentify language exactly
                Dictionary<string, int> countLang = new Dictionary<string, int>();
                int max = 0;
                foreach (KeyValuePair<string, string> lang in transContent)
                {
                    string key = lang.Value;
                    if (countLang.Keys.Contains(key))
                    {
                        countLang[key]++;
                    }
                    else
                        countLang.Add(key, 1);
                    //Get language with max count and priority language isn't English
                    if (countLang[key] > max)
                    {
                        max = countLang[key];
                        if (language != key &&
                            key != "English")
                            language = key;
                    }
                    else
                    {
                        //priority language isn't English indeed it isn't max
                        if (language != key &&
                            language == "English")
                            language = key;
                    }
                }
            }
            catch (WebException we)
            {
                throw we;
            }
            catch (Exception ex)
            {
                throw ex;
            }

            //Get content
            string[] transResult = new string[2];
            transResult[0] = language;
            transResult[1] = string.Join(" ", transContent.ToArray());
            return transResult;
        }

        /// <summary>
        /// Use alexa rank to indentify the rank and location of the forum
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public Dictionary<string, string> GetAlexaRank(string forumUrl)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            result.Add("rank", "");
            result.Add("location", "");

            string url = string.Format("http://www.alexa.com/search?q={0}&r=home_home&p=bigtop", forumUrl);
            Get(url);

            //detect alexa rank
            //string xpath = ".//ul[@class='traffic-stats']/li[1]/a/text()";
            //string xpath = ".//div[@id='search']/div/div[2]/span[1]/a/text()";
            string xpath = ".//div[@id='search']//div[@class='row']/span[1]/a[contains(@href,'#trafficstats')]/text()";
            HtmlNodeCollection ncRank = HtmlDoc.DocumentNode.SelectNodes(xpath);
            if (ncRank != null)
            {
                foreach (HtmlNode node in ncRank)
                {
                    if (node.InnerHtml.Replace("\n", "") != "No Data")
                        result["rank"] = int.Parse(node.InnerHtml.Replace("\n", ""), System.Globalization.NumberStyles.Number).ToString();
                }

                //detect location of forum
                //xpath = ".//ul[@class='traffic-stats']/li[2]/a";
                //xpath = ".//div[@id='search']/div/div[2]/span[2]/a";
                xpath = ".//div[@id='search']//div[@class='row']/span[2]/a";
                HtmlNodeCollection ncLocation = HtmlDoc.DocumentNode.SelectNodes(xpath);
                if (ncLocation != null)
                {
                    foreach (HtmlNode node in ncLocation)
                        result["location"] = node.Attributes["title"].Value;
                }
            }
            //else
            //{
            //    int i = 0;
            //    i += 1;
            //}
            return result;
        }

        public string DownloadImage(string imageUrl, string localpath, string fileName)
        {
            //Check if imageUrl is remote path or local path
            Regex r = new Regex(@"http\:\/\/");
            Match m = r.Match(imageUrl); //if image url contains pattern as "http://" 
            if (m.Success) //remote path
            {
                HttpWebRequest webRequest = HttpWebRequest.Create(imageUrl) as HttpWebRequest;
                HttpWebResponse resp = webRequest.GetResponse() as HttpWebResponse;
                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    if (resp.ContentType.Contains("image/"))
                    {
                        int idx = resp.ContentType.IndexOf("/");
                        if (string.IsNullOrEmpty(fileName))
                            fileName = resp.ResponseUri.Segments[resp.ResponseUri.Segments.Length - 1];
                        fileName = string.Format("{0}\\{1}.{2}", localpath, fileName, resp.ContentType.Substring(idx + 1));
                        byte[] imageContent = ProcessImageStream(resp);
                        FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
                        fs.Write(imageContent, 0, imageContent.Length);
                        fs.Close();
                    }
                }
            }
            else //local path
            {
                fileName = imageUrl;
            }
            return fileName;
        }

        private byte[] ProcessImageStream(HttpWebResponse resp)
        {
            Int64 iContentLength = resp.ContentLength;
            byte[] streamContent;
            MemoryStream memStream = new MemoryStream();
            const int BUFFER_SIZE = 4096;
            int iRead = 0;
            int idx = 0;
            Int64 iSize = 0;
            memStream.SetLength(BUFFER_SIZE);
            try
            {
                using (memStream)
                {
                    while (true)
                    {
                        iRead = 0;
                        byte[] respBuffer = new byte[BUFFER_SIZE];
                        iRead = resp.GetResponseStream().Read(respBuffer, 0, BUFFER_SIZE);
                        if (iRead == 0)
                        { break; }
                        iSize += iRead;
                        memStream.SetLength(iSize);
                        memStream.Write(respBuffer, 0, iRead);
                        idx += iRead;
                    }
                    streamContent = memStream.ToArray();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
            return streamContent;
        }
    }
}